﻿using Microsoft.Phone.Controls;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using WPNextTools.NHelper;

namespace WPNextTools.NControls
{
    [TemplateVisualState(Name = PullDownToRefreshPanel.InactiveVisualState, GroupName = PullDownToRefreshPanel.ActivityVisualStateGroup)]
    [TemplateVisualState(Name = PullDownToRefreshPanel.PullingDownVisualState, GroupName = PullDownToRefreshPanel.ActivityVisualStateGroup)]
    [TemplateVisualState(Name = PullDownToRefreshPanel.ReadyToReleaseVisualState, GroupName = PullDownToRefreshPanel.ActivityVisualStateGroup)]
    [TemplateVisualState(Name = PullDownToRefreshPanel.RefreshingVisualState, GroupName = PullDownToRefreshPanel.ActivityVisualStateGroup)]
    public class PullDownToRefreshPanel : Control
    {
        #region Visual state name constants

        private const string ActivityVisualStateGroup = "ActivityStates";
        private const string InactiveVisualState = "Inactive";
        private const string PullingDownVisualState = "PullingDown";
        private const string ReadyToReleaseVisualState = "ReadyToRelease";
        private const string RefreshingVisualState = "Refreshing";

        #endregion

        #region Fields
        private ScrollViewer _targetScrollViewer;
        private string _activityState;
        private GestureListener _gestrueListener;
        private Point _startPoint;
        #endregion

        #region Event
        public event EventHandler ScrollPushed;
        #endregion


        public PullDownToRefreshPanel()
        {
            this.DefaultStyleKey = typeof(PullDownToRefreshPanel);
            this.LayoutUpdated += PullDownToRefreshPanel_LayoutUpdated;
        }

        void PullDownToRefreshPanel_LayoutUpdated(object sender, EventArgs e)
        {
            this.LayoutUpdated -= PullDownToRefreshPanel_LayoutUpdated;
            if (this._targetScrollViewer == null)
            {
                this._targetScrollViewer = FindFirstChildOfType<ScrollViewer>(VisualTreeHelper.GetParent(this));
                if (_targetScrollViewer == null)
                {
                    this._targetScrollViewer = this.FindAncestor<ScrollViewer>();
                    if (_targetScrollViewer == null)
                    {
                        return;
                    }
                }
                _gestrueListener = GestureService.GetGestureListener(_targetScrollViewer);
                if (_gestrueListener != null)
                {
                    _gestrueListener.DragDelta -= _gestrueListener_DragDelta;
                    _gestrueListener.DragDelta += _gestrueListener_DragDelta;

                    _gestrueListener.DragStarted -= _gestrueListener_DragStarted;
                    _gestrueListener.DragStarted += _gestrueListener_DragStarted;

                    _gestrueListener.DragCompleted -= _gestrueListener_DragCompleted;
                    _gestrueListener.DragCompleted += _gestrueListener_DragCompleted;
                }
            }
            
        }

        void _gestrueListener_DragCompleted(object sender, DragCompletedGestureEventArgs e)
        {
            if (e == null || e.OriginalSource == null) return;
            if (!this.IsRefreshing && this._activityState == PullDownToRefreshPanel.ReadyToReleaseVisualState)
            {
                EventHandler hanlder = this.ScrollPushed;
                if (hanlder != null)
                {
                    hanlder(this, EventArgs.Empty);
                }
            }
            this._activityState = PullDownToRefreshPanel.InactiveVisualState;
            VisualStateManager.GoToState(this, _activityState, false);
            this.PullDistance = 0;
            this.PullFraction = 0;
        }

        void _gestrueListener_DragStarted(object sender, DragStartedGestureEventArgs e)
        {
            try
            {
                _startPoint = e.GetPosition((UIElement)e.OriginalSource);
            }
            catch (System.Exception ex)
            {
                Debug.WriteLine(ex.Message.ToString());
            }
        }

        void _gestrueListener_DragDelta(object sender, DragDeltaGestureEventArgs e)
        {
            try
            {
                Point movePoint = e.GetPosition((UIElement)e.OriginalSource);
                double movePosition = movePoint.Y - _startPoint.Y;
                //Debug.WriteLine("position move ---->" + movePosition);
                //Debug.WriteLine("ViewportHeight" + _targetScrollViewer.ViewportHeight);
                //Debug.WriteLine("ScrollableHeight" + _targetScrollViewer.ScrollableHeight);
                //Debug.WriteLine("ExtentHeight" + _targetScrollViewer.ExtentHeight);
                //Debug.WriteLine("VerticalOffset" + _targetScrollViewer.VerticalOffset);

                if (_targetScrollViewer.VerticalOffset < 0.43 && e.Direction == Orientation.Vertical && !IsRefreshing)
                {
                    if (movePosition > PullThreshold)
                    {
                        //Debug.WriteLine("ReadyToReleaseVisualState-----" + movePosition.ToString());
                        this.PullDistance = movePosition;
                        this.PullFraction = 1.0;
                        _activityState = PullDownToRefreshPanel.ReadyToReleaseVisualState;
                    }
                    else if (movePosition > 0)
                    {
                        //Debug.WriteLine("PullingDownVisualState-----" + movePosition.ToString());
                        this.PullDistance = movePosition;
                        double threshold = this.PullThreshold;
                        this.PullFraction = threshold == 0.0 ? 1.0 : Math.Min(1.0, movePosition / threshold);
                        _activityState = PullDownToRefreshPanel.PullingDownVisualState;
                    }
                    else
                    {
                        //Debug.WriteLine("InactiveVisualState-----" + movePosition.ToString());
                        this.PullDistance = 0;
                        this.PullFraction = 0;
                        _activityState = PullDownToRefreshPanel.InactiveVisualState;
                    }
                }
                else
                {
                    //Debug.WriteLine("InactiveVisualState-----" + movePosition.ToString());
                    this.PullDistance = 0;
                    this.PullFraction = 0;
                    _activityState = PullDownToRefreshPanel.InactiveVisualState;
                }
                VisualStateManager.GoToState(this, _activityState, false);
            }
            catch
            {
            	
            }
        }
       
        #region DependencyProperty

        #region IsRefreshingProperty

        public bool IsRefreshing
        {
            get { return (bool)GetValue(IsRefreshingProperty); }
            set { SetValue(IsRefreshingProperty, value); }
        }

        public static readonly DependencyProperty IsRefreshingProperty =
            DependencyProperty.Register("IsRefreshing", typeof(bool), typeof(PullDownToRefreshPanel), new PropertyMetadata(false, new PropertyChangedCallback(OnIsRefreshingChanged)));

        private static void OnIsRefreshingChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            ((PullDownToRefreshPanel)d).OnIsRefreshingChanged(e);

        }

        private void OnIsRefreshingChanged(DependencyPropertyChangedEventArgs e)
        {
            string activityState = (bool)e.NewValue ? PullDownToRefreshPanel.RefreshingVisualState : PullDownToRefreshPanel.InactiveVisualState;
            VisualStateManager.GoToState(this, activityState, false);
        }

        #endregion

        #region PullThreshold DependencyProperty

        public double PullThreshold
        {
            get { return (double)GetValue(PullThresholdProperty); }
            set { SetValue(PullThresholdProperty, value); }
        }

        public static readonly DependencyProperty PullThresholdProperty =
            DependencyProperty.Register("PullThreshold", typeof(double), typeof(PullDownToRefreshPanel), new PropertyMetadata(200.0));

        #endregion

        #region PullDistance DependencyProperty

        public double PullDistance
        {
            get { return (double)GetValue(PullDistanceProperty); }
            set { SetValue(PullDistanceProperty, value); }
        }

        public static readonly DependencyProperty PullDistanceProperty =
            DependencyProperty.Register("PullDistance", typeof(double), typeof(PullDownToRefreshPanel), new PropertyMetadata(0.0));


        #endregion

        #region PullFraction DependencyProperty

        public double PullFraction
        {
            get { return (double)GetValue(PullFractionProperty); }
            set { SetValue(PullFractionProperty, value); }
        }

        public static readonly DependencyProperty PullFractionProperty =
            DependencyProperty.Register("PullFraction", typeof(double), typeof(PullDownToRefreshPanel), new PropertyMetadata(0.0));


        #endregion

        #region PullingDownTemplate DependencyProperty

        public DataTemplate PullingDownTemplate
        {
            get { return (DataTemplate)GetValue(PullingDownTemplateProperty); }
            set { SetValue(PullingDownTemplateProperty, value); }
        }

        public static readonly DependencyProperty PullingDownTemplateProperty =
            DependencyProperty.Register("PullingDownTemplate", typeof(DataTemplate), typeof(PullDownToRefreshPanel), null);

        #endregion

        #region ReadyToReleaseTemplate DependencyProperty

        public DataTemplate ReadyToReleaseTemplate
        {
            get { return (DataTemplate)GetValue(ReadyToReleaseTemplateProperty); }
            set { SetValue(ReadyToReleaseTemplateProperty, value); }
        }

        public static readonly DependencyProperty ReadyToReleaseTemplateProperty =
            DependencyProperty.Register("ReadyToReleaseTemplate", typeof(DataTemplate), typeof(PullDownToRefreshPanel), null);

        #endregion

        #region RefrehingTemplate DependencyProperty


        //public DataTemplate RefreshingTemplate
        //{
        //    get { return (DataTemplate)GetValue(RefreshingTemplateProperty); }
        //    set { SetValue(RefreshingTemplateProperty, value); }
        //}

        //public static readonly DependencyProperty RefreshingTemplateProperty =
        //    DependencyProperty.Register("RefreshingTemplate", typeof(DataTemplate), typeof(PullDownToRefreshPanel), null);


        #endregion

        #endregion

        #region UtilMethod

        /// <summary>
        /// Performs a breadth-first search of all elements in the container,
        /// and returns the first element encountered that has type T.
        /// </summary>
        /// <typeparam name="T">Type of element to search for.</typeparam>
        /// <param name="initial">The container to search</param>
        /// <returns>Element of the requested type, or null if none was found.</returns>
        private static T FindVisualElement<T>(DependencyObject container) where T : DependencyObject
        {
            Queue<DependencyObject> childQueue = new Queue<DependencyObject>();
            childQueue.Enqueue(container);

            while (childQueue.Count > 0)
            {
                DependencyObject current = childQueue.Dequeue();

                T result = current as T;
                if (result != null && result != container)
                {
                    return result;
                }

                int childCount = VisualTreeHelper.GetChildrenCount(current);
                for (int childIndex = 0; childIndex < childCount; childIndex++)
                {
                    childQueue.Enqueue(VisualTreeHelper.GetChild(current, childIndex));
                }
            }

            return null;
        }

        private static T FindFirstChildOfType<T>(DependencyObject root) where T : class
        {
            // Enqueue root node
            var queue = new Queue<DependencyObject>();
            queue.Enqueue(root);
            while (0 < queue.Count)
            {
                // Dequeue next node and check its children
                var current = queue.Dequeue();
                for (var i = VisualTreeHelper.GetChildrenCount(current) - 1; 0 <= i; i--)
                {
                    var child = VisualTreeHelper.GetChild(current, i);
                    var typedChild = child as T;
                    if (null != typedChild)
                    {
                        return typedChild;
                    }
                    // Enqueue child
                    queue.Enqueue(child);
                }
            }
            // No children match
            return null;
        }
        #endregion
    }
}
